package com.csap.framework.oauth2.server.service;

import cn.dev33.satoken.LoginUtil;
import cn.dev33.satoken.config.SaTokenConfig;
import cn.dev33.satoken.oauth2.model.AccessTokenModel;
import com.csap.framework.extension.result.ResultParam;
import com.csap.framework.extension.result.ResultUtil;
import com.csap.framework.oauth2.Oauth2Enum;
import com.csap.framework.oauth2.UserInfo;
import com.csap.framework.oauth2.request.RegisterRequest;
import com.csap.framework.oauth2.request.UpdateRequest;
import com.csap.framework.oauth2.request.UserRolesRequest;
import com.csap.framework.oauth2.response.RegisterResponse;
import com.csap.framework.oauth2.server.Oauth2ServerProperties;
import com.csap.framework.oauth2.server.entity.UserInfoEntity;
import com.csap.framework.oauth2.server.model.UserAttributeModel;
import com.csap.framework.oauth2.server.model.UserInfoModel;
import com.csap.framework.oauth2.server.service.admin.AdminUserRoleService;
import com.csap.framework.util.Assert;
import com.csap.framework.util.CollectionsUtil;
import com.csap.framework.util.StringUtil;
import com.csap.framework.util.bean.BeanCopierUtil;
import com.csap.framework.util.exception.ExceptionUtils;
import com.csap.framework.util.optional.Optional;
import org.springframework.integration.redis.util.RedisLockRegistry;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.util.Objects;
import java.util.concurrent.locks.Lock;

/**
 * 授权服务业务处理
 *
 * @author yangchengfu
 * @dataTime 2021年-07月-30日 14:02:00
 **/
@Service
public class Oauth2ServerServiceImpl implements Oauth2ServerService {
    @Resource
    private UserInfoService userInfoService;
    @Resource
    private UserRolesService userRolesService;
    @Resource
    private UserAttributeService userAttributeService;
    @Resource
    private UserOpenInfoService openInfoService;
    @Resource
    private Oauth2ServerProperties oauth2ServerProperties;
    @Resource
    private RedisLockRegistry redisLockRegistry;
    @Resource
    private SaTokenConfig saTokenConfig;
    @Resource
    private AdminUserRoleService adminUserRoleService;

    @Override
    public ResultParam<AccessTokenModel> login(String userName, String platform, String password, String deviceType, String clientId) {
        return userInfoService.lambdaQuery()
                .select(UserInfoEntity::getUserId)
                .eq(UserInfoEntity::getPlatform, platform)
                .eq(UserInfoEntity::getUserType, LoginUtil.userType())
                .eq(UserInfoEntity::getUserName, userName)
                .eq(UserInfoEntity::getPassword, password)
                .oneOptional()
                .isNullThrow(ExceptionUtils.mpe(Oauth2Enum.USER_USER_NAME_OR_PASSWORD_ERROR))
                .map(UserInfoEntity::getUserId)
                .value(i -> ResultUtil.success(accessToken(i, clientId, "", LoginUtil.getInstance(LoginUtil.loginType()).login(i, deviceType, platform)).setLoginType(LoginUtil.loginType())));
    }

    @Override
    public AccessTokenModel loginMobile(String mobile, String platform, String clientId, String deviceType, String scope) {
        return userInfoService.lambdaQuery()
                .select(UserInfoEntity::getUserId)
                .eq(UserInfoEntity::getPlatform, platform)
                .eq(UserInfoEntity::getUserType, LoginUtil.userType())
                .eq(UserInfoEntity::getMobile, mobile)
                .oneOptional()
                .isNullThrow(ExceptionUtils.mpe(Oauth2Enum.USER_NOT_EXITES))
                .map(UserInfoEntity::getUserId)
                .map(i -> accessToken(i, clientId, scope, LoginUtil.getInstance(LoginUtil.loginType()).login(i, deviceType, platform)).setLoginType(LoginUtil.loginType()))
                .get();
    }

    @Override
    public Boolean isRegister(RegisterRequest registerRequest, Boolean notNullThrow) {
        return !userInfoService.lambdaQuery()
                .select(UserInfoEntity::getUserName, UserInfoEntity::getUserId)
                .eq(UserInfoEntity::getPlatform, registerRequest.getPlatform())
                .eq(UserInfoEntity::getUserType, LoginUtil.userType())
                .eq(UserInfoEntity::getUserName, registerRequest.getUserName())
                .oneOptional()
                .isNotNullConditionThrow(i -> notNullThrow, ExceptionUtils.mpe(Oauth2Enum.USER_EXITES))
                .then(i -> registerRequest.setQueryUserId(i.getUserId()))
                .value(Objects::isNull);
    }

    @Override
    public UserInfo userInfo(Object loginId) {
        Assert.notNull(loginId, "登录的ID不能为空");
        return Optional.ofNullable(userInfoService.userInfo(loginId, oauth2ServerProperties.getCacheTime()))
                .then(i -> i.setUserAttribute(userInfoService.userAttribute(loginId.toString(), oauth2ServerProperties.getCacheTime())))
                .then(i -> userInfoService.rolePermissions(loginId.toString(), i, oauth2ServerProperties.getCacheTime()))
                .get();
    }

    @Transactional(rollbackFor = Exception.class, propagation = Propagation.REQUIRED)
    @Override
    public RegisterResponse register(RegisterRequest registerRequest) {
        Assert.notEmpty(registerRequest.getUserName(), "登录账号不能为空");
        Assert.isEq(saTokenConfig.getUserTypes().contains(LoginUtil.userType()), false, "用户类型不存在");
        return Optional.of(BeanCopierUtil.copy(registerRequest, UserInfoModel.class))
                .then(i -> i.setUserType(LoginUtil.userType()))
                .then(i -> i.setPassword(oauth2ServerProperties.encry(i.getPassword())))
                .when(i -> StringUtil.isEmpty(registerRequest.getNickName()), i -> i.setNickName(i.getUserName()))
                .then(userInfoService::insert)
                .then(i -> registerRequest.setQueryUserId(i.getUserId()))
                .then(i -> initAttributeAndRole(registerRequest))
                .map(i -> BeanCopierUtil.copy(i, RegisterResponse.class))
                .then(i -> i.setOpenId(registerRequest.getOpenId()))
                .get();
    }

    @Override
    public void loginRegister(RegisterRequest registerRequest) {
        Assert.notEmpty(registerRequest.getUserName(), "用户名不能为空");
        Lock lock = redisLockRegistry.obtain(saTokenConfig.getTokenName() + String.format(REGISTER_LOCK_KEY, registerRequest.getUserName()));
        lock.lock();
        try {
            if (!isRegister(registerRequest, Boolean.FALSE)) {
                register(registerRequest);
            } else {
                String openId;
                if (StringUtil.isEmpty(openId = openInfoService.containsOpenId(registerRequest.getClientId(), registerRequest.getQueryUserId()))) {
                    initAttributeAndRole(registerRequest);
                } else {
                    registerRequest.setOpenId(openId);
                }
            }
        } finally {
            lock.unlock();
        }

    }

    /**
     * 初始化属性和角色
     *
     * @param registerRequest 请求参数
     */
    @Transactional(rollbackFor = Exception.class, propagation = Propagation.REQUIRED)
    public void initAttributeAndRole(RegisterRequest registerRequest) {
        registerRequest.setOpenId(openInfoService.getOpenId(registerRequest.getClientId(), registerRequest.getQueryUserId()));
        userRolesService.addUserRoles(registerRequest.getQueryUserId(), registerRequest.getOpenId(), LoginUtil.platform(), registerRequest.getClientId());
        if (CollectionsUtil.isNotEmpty(registerRequest.getRoleList())) {
            userRolesService.addUserRoles(registerRequest.getRoleList(), registerRequest.getOpenId(), registerRequest.getQueryUserId());
        }
        if (CollectionsUtil.isNotEmpty(registerRequest.getUserAttribute())) {
            UserAttributeModel userAttributeModel = UserAttributeModel.builder()
                    .userId(registerRequest.getQueryUserId())
                    .openId(registerRequest.getOpenId())
                    .build();
            registerRequest.getUserAttribute().forEach(i2 -> {
                userAttributeModel.setName(i2.getName());
                userAttributeModel.setValue(i2.getValue());
                userAttributeService.insert(userAttributeModel);
            });
        }
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public Boolean updateByOpenId(UpdateRequest updateRequest) {
        Assert.isFalse(StringUtil.isEmpty(updateRequest.getOpenId()), "openId不能为空");
        return this.updateByUserId(updateRequest.setUserId(openInfoService.getUserId(updateRequest.getClient_id(), updateRequest.getOpenId())));
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public Boolean removeByOpenId(String openId, String clientId) {
        return Optional.of(openInfoService.getUserId(clientId, openId))
                .then(i -> userRolesService.removeByUserId(openId, i))
                .then(i -> userAttributeService.removeByUserId(i, openId))
                .then(i -> openInfoService.removeByOpenId(openId, i))
                .then(LoginUtil.getInstance()::logoutByLoginId)
                .then(userInfoService::removeById)
                .map(this::clearUserCache)
                .get();
    }

    /**
     * 清除用户缓存
     *
     * @param i 删除的标识
     */
    private boolean clearUserCache(String i) {
        userInfoService.removeCache(i);
        return Boolean.TRUE;
    }

    @Transactional(rollbackFor = Exception.class, propagation = Propagation.REQUIRED)
    @Override
    public Boolean updateByUserId(UpdateRequest updateRequest) {
        Assert.isFalse(StringUtil.isEmpty(updateRequest.getUserId()), "userId不能为空");
        return Optional.of(BeanCopierUtil.copy(updateRequest, UserInfoModel.class))
                .then(userInfoService::validateUpdateUserName)
                .when(i -> {
                    if (userAttributeService.updateAttribute(updateRequest.getUserAttribute(), updateRequest.getOpenId(), i.getUserId())) {
                        userInfoService.userAttribute(i.getUserId(), oauth2ServerProperties.getCacheTime(), Boolean.TRUE);
                    }
                })
                .when(i -> CollectionsUtil.isNotEmpty(updateRequest.getRoleList()), i -> {
                    if (adminUserRoleService.addUserRoles(UserRolesRequest.builder()
                            .openId(updateRequest.getOpenId())
                            .roles(updateRequest.getRoleList())
                            .type(updateRequest.getRoleType())
                            .typeId(updateRequest.getRoleTypeId())
                            .build(), i.getUserId())) {
                        userInfoService.roleList(i.getUserId(), oauth2ServerProperties.getCacheTime(), Boolean.TRUE);
                    }
                })
                .when(i -> userInfoService.userInfo(i.getUserId(), oauth2ServerProperties.getCacheTime(), Boolean.TRUE))
                .when(i -> StringUtil.isNotEmpty(i.getPassword()), i -> i.setPassword(oauth2ServerProperties.encry(i.getPassword())))
                .value(i -> userInfoService.update(i));
    }
}
